package api

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/go-caipu-team/go-caipu-plugin-sdk/backend"
	"github.com/gin-gonic/gin"
	"go-caipu/pkg/api/helper"
	"go-caipu/pkg/plugins"
	"go-caipu/pkg/plugins/backendplugin"
	"go-caipu/pkg/util"
	"gorm.io/gorm/utils"
	"net/http"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"
)

const (
	SocialType = "扫码登录"
)

type Social struct {
	Source      string `json:"source"`
	Description string `json:"description"`
	Logo        string `json:"logo"`
	Status      bool   `json:"status"` //用户是否绑定
}

// GetSocials 获取社交平台登录列表
func (hs *HTTPServer) GetSocials(c *gin.Context) {
	pDto := hs.pluginStore.Plugins(c.Request.Context())
	var items = make([]Social, 0)
	currUserId := hs.getJwtUserID(c)
	var thirdTypes []string
	if currUserId > 0 {
		thirdTypes, _ = hs.userService.GetUserThird(c.Request.Context(), currUserId)
	}

	for _, p := range pDto {
		if !utils.Contains(p.Tags, SocialType) {
			continue
		}
		items = append(items, Social{
			Source:      p.ID,
			Description: p.Name,
			Logo:        p.Info.Logos.Small,
			Status:      utils.Contains(thirdTypes, p.ID), //用户是否绑定，登录页面不使用
		})
	}
	helper.SuccessWithData(c, items)
}

type PluginPath struct {
	Name       string `json:"name"`
	Entry      string `json:"entry"`
	ActiveRule string `json:"activeRule"`
}

// GetMicroApps  前台微服务使用 micro apps地址前台使用
func (hs *HTTPServer) GetMicroApps(c *gin.Context) {
	pDto := hs.pluginStore.Plugins(c.Request.Context())
	var items = make([]PluginPath, 0)
	for _, p := range pDto {
		if p.Name == "" {
			continue
		}

		items = append(items, PluginPath{
			Name:       p.ID,
			Entry:      "/app/" + p.ID + "/",
			ActiveRule: fmt.Sprintf("/app/%s", p.ID),
		})
	}
	helper.SuccessWithData(c, items)
}

func translatePluginRequestErrorToAPIError(err error) error {
	if errors.Is(err, backendplugin.ErrPluginNotRegistered) {
		return backendplugin.ErrPluginNotRegistered
	}
	if errors.Is(err, backendplugin.ErrMethodNotImplemented) {
		return backendplugin.ErrMethodNotImplemented
	}
	if errors.Is(err, backendplugin.ErrPluginUnavailable) {
		return backendplugin.ErrPluginUnavailable
	}
	return err
}

func (hs *HTTPServer) CheckHealth(c *gin.Context) {
	pluginID := c.Param("pluginId")
	pCtx, found, err := hs.pluginContextProvider.Get(c.Request.Context(), pluginID)
	if err != nil {
		c.JSON(500, fmt.Sprintf("pluginID:%s,  Failed to get plugin settings,err:%s", pluginID, err))
		return
	}
	if !found {
		c.JSON(404, fmt.Sprintf("Plugin( %s ) not found", pluginID))
		return
	}
	resp, err := hs.pluginClient.CheckHealth(c.Request.Context(), &backend.CheckHealthRequest{
		PluginContext: pCtx,
		Headers:       map[string]string{},
	})
	if err != nil {
		c.JSON(500, fmt.Sprintf("Plugin( %s ) CheckHealth err:%s", pluginID, err))
		return
	}
	payload := map[string]interface{}{
		"code":    200,
		"success": resp.Status == backend.HealthStatusOk,
		"message": resp.Message,
	}
	// Unmarshal JSONDetails if it's not empty.
	if len(resp.JSONDetails) > 0 {
		var jsonDetails map[string]interface{}
		err = json.Unmarshal(resp.JSONDetails, &jsonDetails)
		if err != nil {
			c.JSON(500, fmt.Sprintf("Failed to unmarshal detailed response from backend plugin:%w", err))
		}
		payload["data"] = jsonDetails
	}
	if resp.Status != backend.HealthStatusOk {
		c.JSON(500, fmt.Sprintf("backend plugin:%s", payload))
		return
	}
	c.JSON(http.StatusOK, payload)
}

// GetPluginList get plugin list
func (hs *HTTPServer) GetPluginList(c *gin.Context) {
	pluginDefinitions := hs.pluginStore.Plugins(c.Request.Context())
	filteredPluginDefinitions := []plugins.PluginDTO{}
	filteredPluginIDs := map[string]bool{}
	//后续过滤操作
	for _, pluginDef := range pluginDefinitions {
		filteredPluginDefinitions = append(filteredPluginDefinitions, pluginDef)
		filteredPluginIDs[pluginDef.ID] = true
	}
	// Prepare DTO
	result := make(PluginList, 0)
	for _, pluginDef := range filteredPluginDefinitions {
		//配置隐藏插件前台不展示
		if hs.Cfg.PluginCfg.PluginSensitives != nil && util.Contains(hs.Cfg.PluginCfg.PluginSensitives, pluginDef.ID) {
			continue
		}
		listItem := PluginListDTO{
			Id:            pluginDef.ID,
			Name:          pluginDef.Name,
			Core:          pluginDef.Class.String(),
			Type:          string(pluginDef.Type),
			Tags:          pluginDef.Tags,
			Enabled:       true,
			Info:          pluginDef.Info,
			Dependencies:  pluginDef.Dependencies,
			LatestVersion: "",
			HasUpdate:     true,
			DefaultNavUrl: "",
		}
		//增加更新检测
		result = append(result, listItem)
	}
	sort.Sort(result)
	helper.SuccessWithData(c, result)
}

// GetPlugin 获取本地安装插件信息
func (hs *HTTPServer) GetPlugin(c *gin.Context) {
	pluginId := c.Param("pluginId")
	pluginDef, exists := hs.pluginStore.Plugin(c.Request.Context(), pluginId)
	if !exists {
		c.JSON(200, helper.Response{
			Code:    40400,
			Success: true,
			Msg:     "插件不存在",
		})
		return
	}
	listItem := PluginListDTO{
		Id:   pluginDef.ID,
		Name: pluginDef.Name,
		Core: pluginDef.Class.String(),

		Type:          string(pluginDef.Type),
		Enabled:       true,
		Info:          pluginDef.Info,
		Dependencies:  pluginDef.Dependencies,
		HasUpdate:     true,
		DefaultNavUrl: "",
	}
	readme, _ := hs.pluginMarkdown(c.Request.Context(), pluginId, pluginDef.Info.Version, "readme")
	changelog, _ := hs.pluginMarkdown(c.Request.Context(), pluginId, pluginDef.Info.Version, "changelog")
	listItem.ReadMe = string(readme)
	listItem.Changelog = string(changelog)

	helper.SuccessWithData(c, listItem)
}

type PluginListDTO struct {
	Name          string                  `json:"name"`
	Type          string                  `json:"type"`
	Core          string                  `json:"core"`
	Id            string                  `json:"id"`
	Tags          []string                `json:"tags"` //关键词数组
	Enabled       bool                    `json:"enabled"`
	Pinned        bool                    `json:"pinned"`
	Info          plugins.Info            `json:"info"`
	Dependencies  plugins.Dependencies    `json:"dependencies"`
	LatestVersion string                  `json:"latestVersion"`
	HasUpdate     bool                    `json:"hasUpdate"`
	DefaultNavUrl string                  `json:"defaultNavUrl"`
	Category      string                  `json:"category"`
	Signature     plugins.SignatureStatus `json:"signature"`
	SignatureType plugins.SignatureType   `json:"signatureType"`
	SignatureOrg  string                  `json:"signatureOrg"`
	ReadMe        string                  `json:"readme,omitempty"`
	Changelog     string                  `json:"changelog,omitempty"`
}
type PluginList []PluginListDTO

func (slice PluginList) Len() int {
	return len(slice)
}

func (slice PluginList) Less(i, j int) bool {
	return slice[i].Name < slice[j].Name
}

func (slice PluginList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

type InstallPluginCommand struct {
	Version string `json:"version"`
}

func (hs *HTTPServer) InsertPlugin(c *gin.Context) {
	//dto := InstallPluginCommand{}
	//if err := helper.BindAndValidate(c, &dto); err != nil {
	//	return
	//}
	pluginID := c.Param("pluginId")
	version := c.Param("version")
	hs.log.Info("Plugin install/update requested", "pluginId", pluginID, hs.getJwtUserID(c))

	compatOps := plugins.NewAddOpts(hs.Cfg.BuildVersion, runtime.GOOS, runtime.GOARCH, "")

	err := hs.pluginInstaller.Add(c.Request.Context(), pluginID, version, compatOps)
	if err != nil {
		helper.ErrorWithDetail(c, http.StatusInternalServerError, "Failed to install plugin", err)
		return
	}
	helper.SuccessWithData(c, "ok")
}

func (hs *HTTPServer) UninstallPlugin(c *gin.Context) {
	pluginID := c.Param("pluginId")

	hs.log.Info("Plugin uninstall requested", "pluginId", pluginID, "user", hs.getUserName(hs.getJwtUserID(c)))
	plugin, exists := hs.pluginStore.Plugin(c.Request.Context(), pluginID)
	if !exists {
		helper.ErrorWithDetail(c, http.StatusInternalServerError, "Failed to uninstall plugin", errors.New("plugin not found"))
		return
	}
	err := hs.RemoveDSByPluginID(plugin)
	if err != nil {
		helper.ErrorWithDetail(c, http.StatusInternalServerError, "Failed to uninstall plugin,remove scripts error", err)
		return
	}

	err = hs.pluginInstaller.Remove(c.Request.Context(), pluginID)
	if err != nil {
		helper.ErrorWithDetail(c, http.StatusInternalServerError, "Failed to uninstall plugin", err)
		return
	}

	helper.SuccessWithData(c, "ok")
}

// GetPluginMarkdown get plugin markdown
func (hs *HTTPServer) GetPluginMarkdown(c *gin.Context) {
	pluginID := c.Param("pluginId")
	name := c.Param("name")
	p, exists := hs.pluginStore.Plugin(c.Request.Context(), pluginID)
	if !exists {
		helper.ErrorWithDetail(c, http.StatusInternalServerError, "plugin not found", errors.New("plugin not found"))
		return
	}
	content, err := hs.pluginMarkdown(c.Request.Context(), pluginID, p.Info.Version, name)

	if err != nil {
		helper.ErrorWithDetail(c, http.StatusInternalServerError, "Could not get markdown file", err)
		return
	}
	if len(content) == 0 {
		content, err = hs.pluginMarkdown(c.Request.Context(), pluginID, p.Info.Version, "readme")
		if err != nil {
			helper.ErrorWithDetail(c, http.StatusInternalServerError, "Could not get markdown file", err)
			return
		}
	}
	c.Header("Content-Type", "text/plain; charset=utf-8")
	c.Writer.Write(content)
}
func (hs *HTTPServer) pluginMarkdown(ctx context.Context, pluginID, pluginVersion, name string) ([]byte, error) {
	file, err := mdFilePath(strings.ToUpper(name))

	if err != nil {
		return make([]byte, 0), err
	}

	md, err := hs.pluginFileStore.File(ctx, pluginID, pluginVersion, file)
	if err != nil {
		if errors.Is(err, plugins.ErrPluginNotInstalled) {
			return make([]byte, 0), plugins.NotFoundError{PluginID: pluginID}
		}

		md, err = hs.pluginFileStore.File(ctx, pluginID, pluginVersion, strings.ToLower(file))
		if err != nil {
			return make([]byte, 0), nil
		}
	}
	return md.Content, nil
}

func mdFilePath(mdFileName string) (string, error) {
	fileExt := filepath.Ext(mdFileName)
	switch fileExt {
	case ".md":
		return util.CleanRelativePath(mdFileName)
	case "":
		return util.CleanRelativePath(fmt.Sprintf("%s.md", mdFileName))
	default:
		return "", errors.New("plugin does not have a markdown file")
	}
}

type StreamData struct {
	C *gin.Context
}

func (d *StreamData) Send(packet *backend.StreamPacket) error {
	fmt.Println("开始接收发送数据")
	var data = packet.Data
	fmt.Println(string(data))

	fmt.Println("结束发送数据结果,输出前台")
	d.C.Writer.Write(data)
	d.C.Writer.(http.Flusher).Flush()
	return nil
}
func (hs *HTTPServer) streamSSE(c *gin.Context) {
	start := time.Now()

	c.Writer.Header().Set("Content-Type", "text/event-stream")
	c.Writer.Header().Set("Cache-Control", "no-cache")
	c.Writer.Header().Set("Connection", "keep-alive")
	c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
	fmt.Printf("接收 request 发送时间：%s\n", time.Now().Sub(start))
	//构建测试请求插件的上下文
	ctx := backend.PluginContext{
		PluginID: "datasource-with-stream",
		DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{
			Name:                    "datasource-with-stream",
			JSONData:                nil,
			DecryptedSecureJSONData: nil,
			Updated:                 time.Time{},
		},
	}
	//客户端发起订阅
	//subscribeReq := &backend.SubscribeStreamRequest{
	//	PluginContext: ctx,
	//	Path:          "stream",
	//	Data:          []byte(`{"filter": "cpu_usage"}`),
	//}
	//fmt.Sprintf("开始 SubscribeStream 发送时间：%s", time.Now().Sub(start))
	//subscribeResp, err := hs.pluginClient.SubscribeStream(c.Request.Context(), subscribeReq)
	//if err != nil {
	//	helper.ErrorWithDetail(c, http.StatusInternalServerError, "Failed to subscribe", err)
	//}
	//if subscribeResp.Status != backend.SubscribeStreamStatusOK {
	//	helper.ErrorWithDetail(c, http.StatusInternalServerError, "Failed to subscribe", errors.New("failed to subscribe"))
	//}
	// fmt.Printf("结束 SubscribeStream 发送时间：%s \n", time.Now().Sub(start))
	//2.系统流运行
	runReq := &backend.RunStreamRequest{
		PluginContext: ctx,
		Path:          "stream",
		Data:          []byte(`{"filter": "cpu_usage"}`),
	}
	result := StreamData{C: c}
	//创建发送器
	sender := backend.NewStreamSender(&result)

	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-c.Request.Context().Done():
			return
		case <-ticker.C:
			//message := "data: " + time.Now().Format(time.RFC3339) + "\n\n"
			//c.Writer.Write([]byte(message))
			//c.Writer.(http.Flusher).Flush()
			fmt.Printf("开始 stream：%s\n", time.Now().Sub(start))
			if err := hs.pluginClient.RunStream(c.Request.Context(), runReq, sender); err != nil {
				helper.ErrorWithDetail(c, http.StatusInternalServerError, "Failed to subscribe", err)
				return
			}
			fmt.Printf("结束 stream：%s\n", time.Now().Sub(start))
		}
	}
}
